#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <glob.h>
#include <stdlib.h>
#include <time.h>
#include <sys/times.h>

#include "rules.h"

#include <rasqal/rasqal.h>

int perfmode = 0;
struct timeval start;
struct timeval end;

void nomatch() {
  gettimeofday(&end, NULL);
  long sec = end.tv_sec - start.tv_sec;
  long usec = end.tv_usec - start.tv_usec;
  if(usec < 0) {
    usec += 1000000;
    sec--;
  }
  float time = ((float) sec) + ((float) usec) / 1000000.0;

  if(!perfmode) {
    printf("Access denied!\n404\n");
  } else {
    printf("denied %f", time);    
  }
  exit(-1);
}

int execute_query(rasqal_world *world, const unsigned char* query,
		  raptor_uri* sources[], int n_sources) {
  const char *ql_name = "sparql";
  char *ql_uri = NULL;
  raptor_uri* base_uri = NULL;
  rasqal_query *rq;
  rasqal_query_results *results;

  rq = rasqal_new_query(world, (const char*) ql_name, 
			(const unsigned char*) ql_uri);
  
  int i;
  for(i=0;i<n_sources;i++) {
    rasqal_query_add_data_graph(rq, sources[i], NULL, 
				RASQAL_DATA_GRAPH_BACKGROUND);
  }

  rasqal_query_prepare(rq, (const unsigned char*) query, base_uri);

  results = rasqal_query_execute(rq);

  raptor_serializer* serializer = NULL;
  const char *serializer_syntax_name = "ntriples";
  rasqal_query_results_formatter* results_formatter = NULL;

  serializer = raptor_new_serializer(serializer_syntax_name);

  FILE *tmpout = fopen("tmp.n3","a");
  raptor_serialize_start_to_file_handle(serializer, base_uri, tmpout);

  int triple_count = 0;
  while(1) {
    raptor_statement *rs = rasqal_query_results_get_triple(results);
    if(!rs)
      break;
    
    raptor_serialize_statement(serializer, rs);
    triple_count++;
    
    if(rasqal_query_results_next_triple(results))
      break;
  }

  raptor_serialize_end(serializer);
  raptor_free_serializer(serializer);

  fclose(tmpout);
  rasqal_free_query(rq); 

  return triple_count; 
}


int main(int argc, char *argv[]) {
  rasqal_world *world;

  world = rasqal_new_world();
  rasqal_world_open(world);

  if(argc < 5) {
    printf("Usage: matcher background policy request requesturi [p]\np changes in performance measurement mode\n");
    exit(-1);
  }
  raptor_uri* background = raptor_new_uri(raptor_uri_filename_to_uri_string(argv[1]));
  raptor_uri* policy = raptor_new_uri(raptor_uri_filename_to_uri_string(argv[2]));
  raptor_uri* request= raptor_new_uri(raptor_uri_filename_to_uri_string(argv[3]));
  const char* request_uri = argv[4];

  if(argc > 5) {
    if(argv[5][0] == 'p') {
      perfmode = 1;
    }
  }

  unsigned char* tmp_uri_str;
  tmp_uri_str = raptor_uri_filename_to_uri_string("tmp.n3");
  raptor_uri* tmp_uri = raptor_new_uri((const unsigned char *) tmp_uri_str);

  raptor_uri* sources[] = {background, policy, request, tmp_uri};
  int n_sources = 4;

  unlink("tmp.n3");
  fclose(  fopen("tmp.n3","w"));

  // Usage / Requests matching
  // start timing

  gettimeofday(&start, NULL);
/*   printf("%jd\n%jd\n",a.tv_sec,a.tv_usec); */
/*   gettimeofday(&a, NULL); */
/*   printf("%jd\n%jd\n",a.tv_sec,a.tv_usec); */
/*   struct tms start; */
/*   times(&start); */
/*   printf("%jd\n%jd\n",start.tms_utime,start.tms_stime); */
/*   printf("%jd\n%jd\n",clock(),clock()); */

  if(execute_query(world, usagereq, sources, 4) > 0) {
    if(execute_query(world, polreq, sources, 4) > 0) {
      //      execute_query(world, accomp, sources, 4);
      // this can be made offline b/c independent of request.
      // now we have to find the request, matching usage + policy
      // and accompanying usages, to create a result
      
      char query[1024];
      snprintf(query, 1024, "prefix p: <http://www.aifb.kit.edu/policy#>\n"
	       "SELECT DISTINCT ?u ?p WHERE { <%s> p:allowedBy ?u . ?u p:perspective ?p } LIMIT 1",
	       request_uri);
      
      rasqal_query *rq;
      rasqal_query_results *results;

      rq = rasqal_new_query(world, "sparql", NULL);
      int i;
      for(i=0;i<n_sources;i++) {
	rasqal_query_add_data_graph(rq, sources[i], NULL,
				    RASQAL_DATA_GRAPH_BACKGROUND);
      }
      rasqal_query_prepare(rq, query, NULL);

      results = rasqal_query_execute(rq);

      char **names;
      rasqal_literal **values;

      if(rasqal_query_results_get_bindings(results,&names,&values) || (values == NULL)) {
	// No match ...
	nomatch();
      }

      char * perspective;
      rasqal_literal * usage;

      if(!strcmp(names[0],"u")) {
	usage = rasqal_new_literal_from_literal(values[0]);
	perspective = malloc(sizeof(char) * (1 + strlen(values[0]->value.uri)));
	strcpy(perspective,values[0]->value.uri);
      } else {
	usage = rasqal_new_literal_from_literal(values[1]);
	perspective = malloc(sizeof(char) * (1 + strlen(values[1]->value.uri)));
	strcpy(perspective,values[1]->value.uri);
      }

      rasqal_free_query(rq);

      snprintf(query, 1024, "prefix p: <http://www.aifb.kit.edu/policy#>\n"
	       "CONSTRUCT { <%sPolicy> p:allows <%s> . <%s> ?p ?o } WHERE "
	       "{ <%s> ?p ?o . FILTER (?p != p:accompaniedBy) }",
	       request_uri, usage->value.uri, usage->value.uri,
	       usage->value.uri);

      rq = rasqal_new_query(world, "sparql", NULL);
      for(i=0;i<n_sources;i++) {
	rasqal_query_add_data_graph(rq, sources[i], NULL,
				    RASQAL_DATA_GRAPH_BACKGROUND);
      }
      rasqal_query_prepare(rq, query, NULL);

      results = rasqal_query_execute(rq);
      FILE *tmpout = fopen("result-policy.n3","w");

      raptor_serializer* serializer = raptor_new_serializer("ntriples");
      raptor_serialize_start_to_file_handle(serializer, NULL, tmpout);
      while(1) {
	raptor_statement *rs = rasqal_query_results_get_triple(results);
	if(!rs)
	  break;
		
	raptor_serialize_statement(serializer, rs);
    
	if(rasqal_query_results_next_triple(results))
	  break;
      }
      
      raptor_serialize_end(serializer);
      raptor_free_serializer(serializer);

      fclose(tmpout);
      rasqal_free_query(rq); 

      
      gettimeofday(&end, NULL);
      long sec = end.tv_sec - start.tv_sec;
      long usec = end.tv_usec - start.tv_usec;
      if(usec < 0) {
	usec += 1000000;
	sec--;
      }
      float time = ((float) sec) + ((float) usec) / 1000000.0;

      //      struct tms end;
      //      times(&end);

      //      float time = ((float) end.tms_utime - start.tms_utime + end.tms_stime - start.tms_stime);
      //      time = time / 1000.0;

      if(!perfmode) {
	printf("Access to perspective %s granted.\nPolicy to be found in result-policy.n3\nElapsed time: %f\n", perspective, time);
      } else {
	printf("granted %f", time);
      }

    } else {
      // No match ...
      nomatch();
    }
  } else {
    // No match ...
    nomatch();
  }





  
  rasqal_free_world(world);
}
